
/**
 * todo 模拟vite实现原理
 */
const fs = require('fs')
const Koa = require('koa')
const path  = require('path')
const compileSfc = require('@vue/compiler-sfc')
const compileDom = require('@vue/compiler-dom')

const app = new Koa()

// 目的是改造.js里面的内容,不是./、../， 替换成@nodules/
function rewirteImport (content) {
  return content.replace(/ from ['|"]([^'"]+)['|"]/g, function (t0, t1) {
    if (t1[0] !== '.' && t1[1] !== '/') {
      return ` from '/@modules/${t1}'`
    } else {
      return t0
    }
  })
}

app.use(async ctx => {
  // 获取请求参数和url
  const { url, query } = ctx.request
  // console.log('query', query.type)
  if (url === '/') {
    // 访问根目录，渲染index.html
    let content = fs.readFileSync('./index.html', 'utf-8')
    // 解决 ReferenceError: process is not defined
    content = content.replace('<script',
    `
    <script>
    window.process = {
      env: {NODE_EV: 'dev'}
    }
    </script>
    <script
    `
    
    )
    // 指定加载类型
    ctx.type = 'text/html'
    ctx.body = content
  } else if (url.endsWith('.js')) {
    // 识别不了main.js里面的语法，指定找不到就去node_modules里面找
    const p = path.resolve(__dirname, url.slice(1))
    ctx.type = 'application/javascript'
    const content = fs.readFileSync(p, 'utf-8')
    ctx.body = rewirteImport(content)
  } else if (url.endsWith('.css')) { // 解析.css文件
    const p = path.resolve(__dirname, url.slice(1))
    const file = fs.readFileSync(p, 'utf-8')
    const content = `
    const css = "${file.replace(/\n/g ,'')}"
    const link = document.createElement('style')
    link.setAttribute('type', 'text/css')
    link.innerHTML = css
    export default css
    `
    ctx.type = 'application/javascript'
    ctx.body = content
  } else if (url.startsWith('/@modules/')) {
    // 解决是/@modules/路径的
    // 这个模块不是本地模块，而是node_modules里面查找
    const prefix = path.resolve(__dirname, 'node_modules', url.replace('/@modules/', ''))
    // 从node_modules里面拿到对应的文件资源
    const module = require(prefix + '/package.json').module
    // console.log('module' , module)
    // 拼接获取到资源后，放入到
    const p = path.resolve(prefix, module)
    // console.log(p)
    const ret = fs.readFileSync(p, 'utf-8')
    ctx.type = 'application/javascript'
    ctx.body = rewirteImport(ret)
  } else if (url.indexOf('.vue') > -1) {
    // 单文件组件解析
    const p = path.resolve(__dirname, url.split('?')[0].slice(1))
    // 解析单文件组件，需要官方的库, @vue/compiler-sfc 这个库解析.vue文件，能让浏览器识别.vue文件
    const { descriptor} =  compileSfc.parse(fs.readFileSync(p, 'utf-8'))
    // console.log('descriptor', descriptor)
    if (!query.type) {
      // js内容
      ctx.type = 'application/javascript'
      const result = `
      ${rewirteImport(descriptor.script.content.replace('export default', 'const __script = '))}
import {render as __render} from "${url}?type=template"
__script.render = __render
export default __script
      `
      ctx.body = result
    } else if (query.type == "template") { // 解析我们的template 编程render函数

      const template = descriptor.template
      // console.log('template', template);
      // @vue/compiler-dom 解析descriptor.template里面的dom树，并浏览器能识别
      const render = compileDom.compile(template.content, {
        mode: "module"
      }).code
      ctx.type = 'application/javascript'
      ctx.body = rewirteImport(render)
    }
  }
})

app.listen(8888, () =>{
  console.log(`启动成功，端口号8888`);
})